﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using StripeOne.Core.S1Exceptions;

namespace StripeOne.Core.S1Tools
{
    public static class S1Reflection
    {
        private static Dictionary<string, Assembly> CachedAssemblies = new Dictionary<string, Assembly>();
        private static Dictionary<string, Type> CachedTypes = new Dictionary<string, Type>();
        public static Assembly GetAssemblyFromBin(string assemblyName)
        {
            if (S1Reflection.CachedAssemblies.ContainsKey(assemblyName))
            {
                return S1Reflection.CachedAssemblies[assemblyName];
            }
            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Assembly[] array = assemblies;
            for (int i = 0; i < array.Length; i++)
            {
                Assembly assembly = array[i];
                string b = assembly.FullName.Substring(0, assembly.FullName.IndexOf(","));
                if (assemblyName == b)
                {
                    S1Reflection.CachedAssemblies[assemblyName] = assembly;
                    return assembly;
                }
            }
            string text = AppDomain.CurrentDomain.BaseDirectory;
            text = Path.Combine(text, "bin");
            text = Path.Combine(text, assemblyName + ".dll");
            if (File.Exists(text))
            {
                return S1Reflection.GetAssemblyFromFile(text);
            }
            string text2 = AppDomain.CurrentDomain.BaseDirectory;
            text2 = Path.Combine(text2, assemblyName + ".dll");
            if (File.Exists(text2))
            {
                return S1Reflection.GetAssemblyFromFile(text2);
            }
            throw new ArgumentException("Could not locate assembly \"" + assemblyName + "\" in current AppDomain", "assemblyName");
        }
        public static Assembly GetAssemblyFromGac(string assemblyName)
        {
            if (S1Reflection.CachedAssemblies.ContainsKey(assemblyName))
            {
                return S1Reflection.CachedAssemblies[assemblyName];
            }
            Assembly result;
            try
            {
                Assembly assembly;
                if (assemblyName.ToLower().IndexOf("version=") >= 0 && assemblyName.ToLower().IndexOf("culture=") >= 0 && assemblyName.ToLower().IndexOf("publickeytoken=") >= 0)
                {
                    assembly = Assembly.Load(assemblyName);
                }
                else
                {
                    assembly = Assembly.Load(assemblyName);
                }
                if (assembly == null)
                {
                    throw new S1ReflectionException("Could not load type from assembly name \"" + assemblyName + "\"", null);
                }
                S1Reflection.CachedAssemblies[assemblyName] = assembly;
                result = assembly;
            }
            catch (Exception innerException)
            {
                throw new S1ReflectionException("Could not load type from assembly name \"" + assemblyName + "\"", innerException);
            }
            return result;
        }
        public static Assembly GetAssemblyFromFile(string fileName)
        {
            if (S1Reflection.CachedAssemblies.ContainsKey(fileName))
            {
                return S1Reflection.CachedAssemblies[fileName];
            }
            Assembly result;
            try
            {
                Assembly assembly = Assembly.LoadFrom(fileName);
                if (assembly != null)
                {
                    S1Reflection.CachedAssemblies[fileName] = assembly;
                }
                result = assembly;
            }
            catch (Exception innerException)
            {
                throw new S1ReflectionException("Could not load assembly from file \"" + fileName + "\"", innerException);
            }
            return result;
        }
        public static Type GetType(Assembly assembly, string className)
        {
            if (S1Reflection.CachedTypes.ContainsKey(assembly.FullName + "." + className))
            {
                return S1Reflection.CachedTypes[assembly.FullName + "." + className];
            }
            Type result;
            try
            {
                Type type = assembly.GetType(className);
                if (type == null)
                {
                    throw new S1ReflectionException("Could not load type \"" + className + "\"", null);
                }
                S1Reflection.CachedTypes[assembly.FullName + "." + className] = type;
                result = type;
            }
            catch (Exception innerException)
            {
                throw new S1ReflectionException("Could not load type \"" + className + "\"", innerException);
            }
            return result;
        }
        public static T GetInstance<T>()
        {
            return (T)S1Reflection.GetInstance(typeof(T));
        }
        public static T GetInstance<T>(Type[] parameterTypes, object[] parameters)
        {
            return (T)S1Reflection.GetInstance(typeof(T), parameterTypes, parameters);
        }
        public static object GetInstance(Type type)
        {
            return S1Reflection.GetInstance(type, new Type[0], new object[0]);
        }
        public static object GetInstance(Type type, Type[] parameterTypes, object[] parameters)
        {
            ConstructorInfo constructor;
            try
            {
                constructor = type.GetConstructor(parameterTypes);
            }
            catch (Exception innerException)
            {
                throw new S1ReflectionException("Could not find constructor for specified parameter types", innerException);
            }
            if (constructor == null)
            {
                throw new S1ReflectionException("Could not find constructor for specified parameter types", null);
            }
            object obj;
            try
            {
                obj = constructor.Invoke(parameters);
            }
            catch (Exception innerException2)
            {
                throw new S1ReflectionException("Could not create an instance with the specified parameters", innerException2);
            }
            if (obj == null)
            {
                throw new S1ReflectionException("Could not create an instance with the specified parameters", null);
            }
            return obj;
        }
        public static string[] GetPropertyNames(object obj)
        {
            Type type = obj.GetType();
            PropertyInfo[] properties = type.GetProperties();
            List<string> list = new List<string>();
            PropertyInfo[] array = properties;
            for (int i = 0; i < array.Length; i++)
            {
                PropertyInfo propertyInfo = array[i];
                list.Add(propertyInfo.Name);
            }
            return list.ToArray();
        }
        public static object GetPropertyValue(object obj, string propertyName)
        {
            Type type = obj.GetType();
            PropertyInfo[] properties = type.GetProperties();
            PropertyInfo[] array = properties;
            int i = 0;
            while (i < array.Length)
            {
                PropertyInfo propertyInfo = array[i];
                if (propertyInfo.Name == propertyName)
                {
                    if (propertyInfo.CanRead)
                    {
                        return propertyInfo.GetValue(obj, null);
                    }
                    throw new S1ReflectionException("Property " + propertyName + " cannot be read", null);
                }
                else
                {
                    i++;
                }
            }
            throw new S1ReflectionException("Property " + propertyName + " does not exist on specified object", null);
        }
        public static T GetPropertyValue<T>(object obj, string propertyName)
        {
            return (T)S1Reflection.GetPropertyValue(obj, propertyName);
        }
        public static void SetPropertyValue(object obj, object value, string propertyName)
        {
            Type type = obj.GetType();
            PropertyInfo[] properties = type.GetProperties();
            PropertyInfo[] array = properties;
            int i = 0;
            while (i < array.Length)
            {
                PropertyInfo propertyInfo = array[i];
                if (propertyInfo.Name == propertyName)
                {
                    if (propertyInfo.CanWrite)
                    {
                        propertyInfo.SetValue(obj, value, null);
                        return;
                    }
                    throw new S1ReflectionException("Property " + propertyName + " cannot be written", null);
                }
                else
                {
                    i++;
                }
            }
            throw new S1ReflectionException("Property " + propertyName + " does not exist on specified object", null);
        }
        public static string[] GetFieldNames(object obj)
        {
            Type type = obj.GetType();
            FieldInfo[] fields = type.GetFields();
            List<string> list = new List<string>();
            FieldInfo[] array = fields;
            for (int i = 0; i < array.Length; i++)
            {
                FieldInfo fieldInfo = array[i];
                list.Add(fieldInfo.Name);
            }
            return list.ToArray();
        }
        public static object GetFieldValue(object obj, string fieldName)
        {
            Type type = obj.GetType();
            FieldInfo[] fields = type.GetFields();
            FieldInfo[] array = fields;
            for (int i = 0; i < array.Length; i++)
            {
                FieldInfo fieldInfo = array[i];
                if (fieldInfo.Name == fieldName)
                {
                    return fieldInfo.GetValue(obj);
                }
            }
            throw new S1ReflectionException("Field " + fieldName + " does not exist on specified object", null);
        }
        public static object GetFieldValue<T>(object obj, string fieldName)
        {
            return (T)S1Reflection.GetFieldValue(obj, fieldName);
        }
        public static void SetFieldValue(object obj, object value, string fieldName)
        {
            Type type = obj.GetType();
            FieldInfo[] fields = type.GetFields();
            FieldInfo[] array = fields;
            for (int i = 0; i < array.Length; i++)
            {
                FieldInfo fieldInfo = array[i];
                if (fieldInfo.Name == fieldName)
                {
                    fieldInfo.SetValue(obj, value);
                    return;
                }
            }
            throw new S1ReflectionException("Field " + fieldName + " does not exist on specified object", null);
        }
        public static void CallMethod(object obj, string methodName, Type[] parameterTypes, object[] parameterValues)
        {
            Type type = obj.GetType();
            MethodInfo method = type.GetMethod(methodName, parameterTypes);
            if (method == null)
            {
                throw new S1ReflectionException("Could not locate method " + methodName + " in type " + type.Name, null);
            }
            method.Invoke(obj, parameterValues);
        }
        public static object GetMethodResult(object obj, string methodName, Type[] parameterTypes, object[] parameterValues)
        {
            Type type = obj.GetType();
            MethodInfo method = type.GetMethod(methodName, parameterTypes);
            if (method == null)
            {
                throw new S1ReflectionException("Could not locate method " + methodName + " in type " + type.Name, null);
            }
            return method.Invoke(obj, parameterValues);
        }
        public static T GetMethodResult<T>(object obj, string methodName, Type[] parameterTypes, object[] parameterValues)
        {
            return (T)S1Reflection.GetMethodResult(obj, methodName, parameterTypes, parameterValues);
        }
        public static string[] GetPropertyNames(Type type)
        {
            PropertyInfo[] properties = type.GetProperties();
            List<string> list = new List<string>();
            PropertyInfo[] array = properties;
            for (int i = 0; i < array.Length; i++)
            {
                PropertyInfo propertyInfo = array[i];
                list.Add(propertyInfo.Name);
            }
            return list.ToArray();
        }
        public static object GetPropertyValue(Type type, string propertyName)
        {
            PropertyInfo[] properties = type.GetProperties();
            PropertyInfo[] array = properties;
            int i = 0;
            while (i < array.Length)
            {
                PropertyInfo propertyInfo = array[i];
                if (propertyInfo.Name == propertyName)
                {
                    if (propertyInfo.CanRead)
                    {
                        return propertyInfo.GetValue(null, null);
                    }
                    throw new S1ReflectionException("Property " + propertyName + " cannot be read", null);
                }
                else
                {
                    i++;
                }
            }
            throw new S1ReflectionException("Property " + propertyName + " does not exist on specified object", null);
        }
        public static T GetPropertyValue<T>(Type type, string propertyName)
        {
            return (T)S1Reflection.GetPropertyValue(type, propertyName);
        }
        public static void SetPropertyValue(Type type, object value, string propertyName)
        {
            PropertyInfo[] properties = type.GetProperties();
            PropertyInfo[] array = properties;
            int i = 0;
            while (i < array.Length)
            {
                PropertyInfo propertyInfo = array[i];
                if (propertyInfo.Name == propertyName)
                {
                    if (propertyInfo.CanWrite)
                    {
                        propertyInfo.SetValue(null, value, null);
                        return;
                    }
                    throw new S1ReflectionException("Property " + propertyName + " cannot be written", null);
                }
                else
                {
                    i++;
                }
            }
            throw new S1ReflectionException("Property " + propertyName + " does not exist on specified object", null);
        }
        public static string[] GetFieldNames(Type type)
        {
            FieldInfo[] fields = type.GetFields();
            List<string> list = new List<string>();
            FieldInfo[] array = fields;
            for (int i = 0; i < array.Length; i++)
            {
                FieldInfo fieldInfo = array[i];
                list.Add(fieldInfo.Name);
            }
            return list.ToArray();
        }
        public static object GetFieldValue(Type type, string fieldName)
        {
            FieldInfo[] fields = type.GetFields();
            FieldInfo[] array = fields;
            for (int i = 0; i < array.Length; i++)
            {
                FieldInfo fieldInfo = array[i];
                if (fieldInfo.Name == fieldName)
                {
                    return fieldInfo.GetValue(null);
                }
            }
            throw new S1ReflectionException("Field " + fieldName + " does not exist on specified object", null);
        }
        public static object GetFieldValue<T>(Type type, string fieldName)
        {
            return (T)S1Reflection.GetFieldValue(type, fieldName);
        }
        public static void SetFieldValue(Type type, object value, string fieldName)
        {
            FieldInfo[] fields = type.GetFields();
            FieldInfo[] array = fields;
            for (int i = 0; i < array.Length; i++)
            {
                FieldInfo fieldInfo = array[i];
                if (fieldInfo.Name == fieldName)
                {
                    fieldInfo.SetValue(null, value);
                    return;
                }
            }
            throw new S1ReflectionException("Field " + fieldName + " does not exist on specified object", null);
        }
        public static void CallMethod(Type type, string methodName, Type[] parameterTypes, object[] parameterValues)
        {
            MethodInfo method = type.GetMethod(methodName, parameterTypes);
            if (method == null)
            {
                throw new S1ReflectionException("Could not locate method " + methodName + " in type " + type.Name, null);
            }
            method.Invoke(null, parameterValues);
        }
        public static object GetMethodResult(Type type, string methodName, Type[] parameterTypes, object[] parameterValues)
        {
            MethodInfo method = type.GetMethod(methodName, parameterTypes);
            if (method == null)
            {
                throw new S1ReflectionException("Could not locate method " + methodName + " in type " + type.Name, null);
            }
            return method.Invoke(null, parameterValues);
        }
        public static T GetMethodResult<T>(Type type, string methodName, Type[] parameterTypes, object[] parameterValues)
        {
            return (T)S1Reflection.GetMethodResult(type, methodName, parameterTypes, parameterValues);
        }
    }
}
